/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/net/sockets/UnixSocket.h"

#ifndef _WIN32
namespace rgcpp
{

UnixSocket::UnixSocket(const std::string& socketPath)
{
	this->releaseForced=false;

	if((socketFD = socket(AF_UNIX,AF_UNIX,0)) == -1)
	{
		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	struct sockaddr_un addr;
	memset(&addr, 0, sizeof(addr));

	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path,socketPath.c_str());

	//Open the socket
	if(::connect(socketFD,(struct sockaddr *)&addr,sizeof(addr)) != 0)
	{
		shutdown(socketFD,SHUT_RDWR);
		close(socketFD);

		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	timeout.tv_usec = NO_TIMEOUT;
	timeout.tv_sec = NO_TIMEOUT;
}

UnixSocket::~UnixSocket()
{
	boost::mutex::scoped_lock lockRead(mutRead);
	boost::mutex::scoped_lock lockWrite(mutWrite);

	::shutdown(socketFD,SHUT_RDWR);
	::close(socketFD);
}

int UnixSocket::readData(char* buffer, const unsigned int length)
{
	boost::mutex::scoped_lock lock(mutRead);

	fd_set fdList;

	FD_ZERO(&fdList);
	FD_SET(socketFD, &fdList);

	int value;
	struct timeval timeout = this->timeout;

	do
	{
		if(!timeout.tv_usec && !timeout.tv_sec)
			value = ::select(socketFD+1,&fdList,NULL,NULL,NULL);
		else
			value = ::select(socketFD+1,&fdList,NULL,NULL,&timeout);
	}while(value == -1&& errno == EINTR);

	if(value == 0)
	{
		Throw(SocketException(std::string("Error reading from unixsocket - Timeout exception")));
	}
	else if(value == -1)
	{
		Throw(SocketException(std::string("Error reading from unixsocket - ")+strerror(errno)));
	}

	if(this->releaseForced)
		return 0;

	int readSize = ::recv(socketFD,buffer,length,MSG_NOSIGNAL);

	if(readSize == 0)
	{
		Throw(SocketException(std::string("Error reading from unixsocket - Disconnected")));
	}
	else if(readSize == -1)
	{
		Throw(SocketException(std::string("Error reading from unixsocket - ")+strerror(errno)));
	}

	return readSize;
}

void UnixSocket::writeData(const char* buffer, const unsigned int length)
{
	if(!length)
		return;

	boost::mutex::scoped_lock lock(mutWrite);

	int writeSize = ::send(socketFD,buffer,length,MSG_NOSIGNAL);

	if(writeSize<=0)
	{
		Throw(SocketException(std::string("Error wrinting to unixsocket - ")+strerror(errno)));
	}

	::fsync(socketFD);
}

void UnixSocket::setTimeout(const unsigned long timeout)
{
	this->timeout.tv_usec = (timeout % 1000) * 1000;
	this->timeout.tv_sec = timeout / 1000;
}

unsigned long UnixSocket::getTimeout()
{
	unsigned long timeout;

	timeout = this->timeout.tv_sec * 1000;
	timeout += this->timeout.tv_usec / 1000;

	return timeout;
}

void UnixSocket::release()
{
	this->releaseForced=true;

	shutdown(socketFD,SHUT_RDWR);
}

bool UnixSocket::check4readData(const unsigned long timeout)
{
	boost::mutex::scoped_lock lock(mutRead);

	struct timeval time;
	fd_set fdList;

	time.tv_usec = (timeout % 1000) * 1000;
	time.tv_sec = timeout / 1000;

	FD_ZERO(&fdList);
	FD_SET(socketFD, &fdList);

	if(select(socketFD+1,&fdList,NULL,NULL,&time) <= 0)
		return false;
	else
		return true;
}

UnixSocket::UnixSocket(int socketFD)
{
	this->socketFD=socketFD;
	this->releaseForced=false;

	timeout.tv_usec = NO_TIMEOUT;
	timeout.tv_sec = NO_TIMEOUT;
}

}
#endif